﻿import mx.utils.Delegate;
import kliment.net.URLMethod;
import kliment.utils._Object;

/**
 * Fast loader/parser XML nodes
 * 
 * Recursively pars XMLNode and create an similar object to the structure of XMLNode
 * 
 * @author	Kliment
 * @version	2.4.9
*/

dynamic class kliment.data.FastXML {
	private static var _ignorKeys:Array = [0, 'onLoad',  'onHTTPStatus', 'addListener', 'removeListener'];
	private var _xmlNode:XMLNode;
	private var _status:Number = 0;
	private var _url:String = '';
	private var _conteinerLoad:LoadVars;
	private var _parent:FastXML;
	private var _contentType:String;
	private var _isLoaded:Boolean = false;
	
	public var attributes:Object = null;
	
	public var onLoad:Function = function(success:Boolean):Void {}
	public var onHTTPStatus:Function = function(httpStatus:Number):Void {}
	
	private var _listeners:Array;
	private var _addListener2:Function;
	
	public function get length():Number { return 1 }
	public function get status():Number { return _status }
	public function get url():String { return _url }
	public function get parent():FastXML { return _parent }
	
	public function FastXML(xmlNode:XMLNode, parent:FastXML) {
		if (parent != null) _parent = parent;
		if (xmlNode instanceof XMLNode){
			_xmlNode = xmlNode;
			_parsNode(_xmlNode);
		}
		this[0] = this;
		addListener = Delegate.create(this, _addListener);
	}
	
	private function _addListener(target:Object):Void {
		_initAsBroadcaster();
		_addListener2(target);
	}
	
	private function _initAsBroadcaster():Void {
		if (!_listeners) {
			AsBroadcaster.initialize(this);
			_addListener2 = addListener;
			addListener = Delegate.create(this, _addListener);
		}
	}
	
	public function get contentType():String { return _contentType; }
	public function set contentType(value:String):Void {
		_contentType = value;
	}
	
	public function get isLoaded():Boolean { return _isLoaded }
	
	public function value():String {
		return toString();
	}
	
	public function toString():String {
		var temp = _xmlNode.firstChild.nodeValue;
		return temp || null;
	}
	
	public function load(url:String):Boolean {
		_isLoaded = false;
		_url = url;
		_conteinerLoad = _createConteiner();
		_conteinerLoad.onData = Delegate.create(this, _onDataHandler);
		_conteinerLoad.onHTTPStatus = Delegate.create(this, _onHTTPStatusHandler);
		return _conteinerLoad.load(url);
	}
	
	public function send(url:String, target:String, method:String):Boolean {
		_url = url;
		var sendVars:LoadVars = _createConteiner();
		sendVars.data = toXMLString('data');
		return sendVars.send(url, target, method);
	}
	
	public function sendAndLoad(url:String,  object:Object, method:String):Boolean {
		_isLoaded = false;
		_url = url;
		var sendVars:LoadVars = new LoadVars();
		_conteinerLoad = _createConteiner();
		_Object.copyTo(object, sendVars);
		_conteinerLoad.onData = Delegate.create(this, _onDataHandler);
		_conteinerLoad.onHTTPStatus = Delegate.create(this, _onHTTPStatusHandler);
		return sendVars.sendAndLoad(url, _conteinerLoad, method||URLMethod.POST);
	}
	
	private function _createConteiner():LoadVars {
		var conteiner = new LoadVars();
		if (_contentType) 
			_conteinerLoad.contentType = _contentType;
		return conteiner;
	}
	
	public function toXML(mainNodeName:String):XML {
		return new XML(toXMLString(mainNodeName));
	}
	
	public function toXMLString(mainNodeName:String):String {
		if (mainNodeName && mainNodeName.length > 0)
			return _toStringNode(mainNodeName, this);
		else 
			return _assembleNode(this);
	}
	
	public function getBytesLoaded():Number {
		return _conteinerLoad.getBytesLoaded();
	}
	
	public function getBytesTotal():Number {
		return _conteinerLoad.getBytesTotal();
	}
	
	private function _onHTTPStatusHandler(httpStatus:Number):Void {
		onHTTPStatus(httpStatus, this);
		broadcastMessage('onHTTPStatus', httpStatus, this);
	}
	
	private function _onDataHandler(src:String):Void {
		var tempXML:XML = new XML();
		tempXML.ignoreWhite = true;
		tempXML.parseXML(src);
		_status = tempXML.status;
		_xmlNode = tempXML.firstChild;
		_parsNode(_xmlNode);
		var isSuccess:Boolean = tempXML.firstChild ? true : false;
		_isLoaded = true;
		onLoad(isSuccess, this);
		broadcastMessage('onLoad', isSuccess, this);
	}
	
	private function _parsNode(xmlNode:XMLNode):Void {
		if (xmlNode.attributes){
			attributes = new Object();
			for (var i in _xmlNode.attributes)
				attributes[i] = xmlNode.attributes[i];
		}
		
		var count:Number = xmlNode.childNodes.length;
		for (var i:Number = 0; i < count; ++i) {
			var node:XML = xmlNode.childNodes[i];
			var nodeName:String = node.nodeName;
			if (node.nodeName) 
				_addValue(nodeName, new FastXML(node, this));
		}
	}
	
	private function _addValue(name:String, value):Void {
		if (this[name]) {
			if (!(this[name] instanceof Array))
				this[name] = new Array(this[name]);
			this[name].push(value);
		} else 
			this[name] = value;
	}
	
	private function _assembleNode(value:FastXML):String {
		var forReturn:String = '';
		for (var i in value)
			if (value[i] instanceof FastXML) {
				if (value[i].value() != null){
					forReturn += _toStringNodeValue(i, value[i]);
				} else if (!_ignoredKey(i)){
					forReturn += _toStringNode(i, value[i]);
				}
			} else if (value[i] instanceof Array) {
				forReturn += _arrayToXMLString(value[i], i);
			}
		return forReturn;
	}
	
	private function _toStringNodeValue(mame:String, value:FastXML):String {
		return "\n<" + mame + _attributes(value) + ">" + value.value() + "</" + mame + ">";
	}
	
	private function _toStringNode(mame:String, value:FastXML):String {
		return "\n<" + mame + _attributes(value) + ">" + _assembleNode(value) + "\n</" + mame + ">";
	}
	
	private function _arrayToXMLString(src:Array, tagName:String):String {
		var forReturn:String = '';
		var len:Number = src.length;
		for (var i:Number = 0; i < len; i++)
			if (src[i].value())
				forReturn += _toStringNodeValue(tagName, src[i]);
			else 
				forReturn += _toStringNode(tagName, src[i]);
		return forReturn;
	}
	
	private function _attributes(src:FastXML):String {
		var forReturn:String = '';
		for (var i in src.attributes)
			forReturn += ' ' + i + '="' + src.attributes[i] + '"';
		return forReturn;
	}
	
	private function _ignoredKey(key):Boolean {
		for (var i in _ignorKeys)
			if (_ignorKeys[i] == key)
				return true;
		return false;
	}
	
	public function remove():Void {
		_xmlNode = null;
		_listeners = null;
		onLoad = null;
		onHTTPStatus = null;
	}
	
	private function broadcastMessage(eventName:String):Void {  }
	public function addListener(traget:Object):Void {  }
	public function removeListener(traget:Object):Void {  }
}